package com.xuecheng.checkcode.service.impl;

import cn.hutool.core.io.resource.ResourceUtil;
import com.xuecheng.base.exception.XCPlusBaseException;
import com.xuecheng.checkcode.exception.EmailSendFaildException;
import com.xuecheng.checkcode.exception.ProcessHTMLFailedException;
import com.xuecheng.checkcode.model.CheckCodeResultDTO;
import com.xuecheng.checkcode.model.GenerateCheckCodeParamsDTO;
import com.xuecheng.checkcode.service.AbstractCheckCodeService;
import com.xuecheng.checkcode.service.CheckCodeService;
import com.xuecheng.checkcode.service.IEmailService;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 勾新杰
 * @version 1.0
 * @description:
 * @date 2025/3/9 21:21
 */
@Component("EmailCheckCodeService")
@RequiredArgsConstructor
public class EmailCheckCodeServiceImpl extends AbstractCheckCodeService implements CheckCodeService {

    private static final Integer CODE_LENGTH = 6;

    private static final Integer EXPIRE_SECONDS = 300;

    private static final String KEY_PREFIX = "email:";

    private final IEmailService emailService;

    /**
     * 设置验证码生成器
     *
     * @param checkCodeGenerator 生成器
     */
    @Resource(name = "NumberCheckCodeGenerator")
    @Override
    public void setCheckCodeGenerator(CheckCodeGenerator checkCodeGenerator) {
        this.checkCodeGenerator = checkCodeGenerator;
    }

    /**
     * 设置验证码key生成器
     *
     * @param keyGenerator key生成器
     */
    @Resource(name = "UUIDKeyGenerator")
    @Override
    public void setKeyGenerator(KeyGenerator keyGenerator) {
        this.keyGenerator = keyGenerator;
    }

    /**
     * 设置验证码存储
     *
     * @param checkCodeStore 验证码存储器
     */
    @Resource(name = "RedisCheckCodeStore")
    @Override
    public void setCheckCodeStore(CheckCodeStore checkCodeStore) {
        this.checkCodeStore = checkCodeStore;
    }

    /**
     * 生成验证码
     *
     * @param generateCheckCodeParamsDTO 验证码参数
     * @return 生成结果
     */
    @Override
    public CheckCodeResultDTO generate(GenerateCheckCodeParamsDTO generateCheckCodeParamsDTO) {
        try {
            // 0. 生成验证码
            GenerateResult result = generate(CODE_LENGTH, KEY_PREFIX, EXPIRE_SECONDS);
            // 1. 获取模板文件并转换为模板
            Configuration configuration = new Configuration(Configuration.getVersion());
            // 用这种模式获取模板文件，即便打成jar包也不会找不到
            configuration.setTemplateLoader(new ClassTemplateLoader(this.getClass().getClassLoader(), "/templates"));
            configuration.setDefaultEncoding(StandardCharsets.UTF_8.name());
            Template template = configuration.getTemplate("check-code.ftl");
            // 2. 获取课程预览数据作为模型数据
            CheckCodeModel model = new CheckCodeModel(result.getCode(), EXPIRE_SECONDS / 60);
            Map<String, Object> map = new HashMap<>();
            map.put("model", model);
            // 3. 将模板文件结合模型数据生成html代码
            String code = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
            // 4. 发送html邮件
            boolean sent = emailService.sendEmail(generateCheckCodeParamsDTO.getParam1(), "验证码", code, true);
            if (!sent) EmailSendFaildException.cast();
            return new CheckCodeResultDTO(result.getKey(), null);
        } catch (IOException | TemplateException e) {
            throw new ProcessHTMLFailedException();
        }
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class CheckCodeModel{

        /**
         * 验证码
         */
        private String code;

        /**
         * 过期时间
         */
        private Integer expire;
    }
}
